#[cfg(test)]
mod panic_test {
    #[test]
    fn call() {
        let v = vec![1, 2, 3];
        println!("{}", v[99]); // 被动panic
        panic!("crash and burn"); //手动调用
    }

    // 当出现 panic! 时，程序提供了两种方式来处理终止流程：栈展开 (默认)和直接终止。
    //  栈展开,Rust 会回溯栈上数据和函数调用，因此也意味着更多的善后工作，好处是可以给出充分的报错信息和栈调用信息，便于事后的问题复盘
    //  直接终止, 资源由操作系统回收
    fn config() {
        // 例如下面的配置修改 Cargo.toml 文件，实现在 release 模式下遇到 panic 直接终止：

        // [profile.release]
        // panic = 'abort'

        // panic 后
        // main 线程 程序会终止
        // 子线程不会影响main 线程
    }
}
/*

panic 原理剖析
本来不想写这块儿内容，因为真的难写，但是转念一想，既然号称圣经，那么本书就得与众不同，避重就轻显然不是该有的态度。

当调用 panic! 宏时，它会

格式化 panic 信息，然后使用该信息作为参数，调用 std::panic::panic_any() 函数
panic_any 会检查应用是否使用了 panic hook，如果使用了，该 hook 函数就会被调用（hook 是一个钩子函数，是外部代码设置的，用于在 panic 触发时，执行外部代码所需的功能）
当 hook 函数返回后，当前的线程就开始进行栈展开：从 panic_any 开始，如果寄存器或者栈因为某些原因信息错乱了，那很可能该展开会发生异常，最终线程会直接停止，展开也无法继续进行
展开的过程是一帧一帧的去回溯整个栈，每个帧的数据都会随之被丢弃，但是在展开过程中，你可能会遇到被用户标记为 catching 的帧（通过 std::panic::catch_unwind() 函数标记），此时用户提供的 catch 函数会被调用，展开也随之停止：当然，如果 catch 选择在内部调用 std::panic::resume_unwind() 函数，则展开还会继续。
还有一种情况，在展开过程中，如果展开本身 panic 了，那展开线程会终止，展开也随之停止。

一旦线程展开被终止或者完成，最终的输出结果是取决于哪个线程 panic：对于 main 线程，操作系统提供的终止功能 core::intrinsics::abort() 会被调用，最终结束当前的 panic 进程；如果是其它子线程，那么子线程就会简单的终止，同时信息会在稍后通过 std::thread::join() 进行收集。
*/
